package com.sunpy.niohttp.annotation.impl;

import com.sunpy.niohttp.annotation.RespBodyJson;
import com.sunpy.niohttp.annotation.ReqBody;
import com.sunpy.niohttp.constant.HTTP_Method;
import com.sunpy.niohttp.file.FileUtil;
import com.sunpy.niohttp.file.HttpPropertiesConfig;
import com.sunpy.niohttp.model.*;
import com.sunpy.niohttp.annotation.ReqPath;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 注解实现类
 */
public class AnnotationImpl {

    private final static String GENERAL_PACKAGE = "com.sunpy.niohttp.user";

    /**
     * 扫描指定包路径下面的方法上面的注解
     * @param packagePath
     * @return uri对应的方法映射
     * @throws Exception
     */
    public static Map<String, MethodMapping> scanAnnotationToMethodMapping(@NotNull String packagePath) throws Exception {
        // 查找指定包下面的所有类
        List<String> clazzNameList = FileUtil.findClassByPackagePath(packagePath);

        if (CollectionUtils.isEmpty(clazzNameList)) {
            return new HashMap<>();
        }

        Map<String, MethodMapping> uriMapping = new HashMap<>();

        for (String clazzName : clazzNameList) {
            Class<?> clazz = Class.forName(packagePath + "." +clazzName);
            Method[] methods = clazz.getMethods();

            for (Method method : methods) {
                if (method != null) {
                    MethodMapping methodMapping = new MethodMapping();

                    // 封装函数信息
                    MethodInfo methodInfo = new MethodInfo();
                    packMethodInfo(packagePath, clazzName, method, methodInfo);

                    // 封装HTTP协议信息
                    HttpInfo httpInfo = new HttpInfo();
                    String uri = packHttpInfo(method, httpInfo);

                    methodMapping.setMethodInfo(methodInfo);
                    methodMapping.setHttpInfo(httpInfo);
                    uriMapping.put(uri, methodMapping);

                }
            }
        }

        return uriMapping;
    }

    /**
     * 封装HTTP协议信息
     * @param method
     * @param httpInfo
     * @return
     */
    private static String packHttpInfo(Method method, HttpInfo httpInfo) {
        if (method.isAnnotationPresent(ReqPath.class)) {
            ReqPath reqPath = method.getAnnotation(ReqPath.class);
            httpInfo.setUri(reqPath.value());
            httpInfo.setHttpMethod(reqPath.method());
            return reqPath.value();
        }

        return null;
    }

    /**
     * 封装函数信息
     * @param packagePath
     * @param clazzName
     * @param method
     * @param methodInfo
     */
    private static void packMethodInfo(String packagePath, String clazzName, Method method, MethodInfo methodInfo) {
        List<AnnotationInfo> annotationInfoList = new ArrayList<>();

        if (method.isAnnotationPresent(ReqPath.class)) {
            /**
             * 处理函数上的@RequestPath注解
             */
            ReqPath reqPath = method.getAnnotation(ReqPath.class);
            AnnotationInfo annotationInfo = new AnnotationInfo();
            annotationInfo.setAnnotationName(ReqPath.class.getName());
            Map<String, String> params = new HashMap<>();
            params.put("value", reqPath.value());
            params.put("method", reqPath.method());
            annotationInfo.setParam(params);
            annotationInfoList.add(annotationInfo);
        }

        /**
         * 处理函数上的@JsonResponseBody注解
         */
        if (method.isAnnotationPresent(RespBodyJson.class)) {
            AnnotationInfo annotationInfo = new AnnotationInfo();
            annotationInfo.setAnnotationName(RespBodyJson.class.getName());
            annotationInfoList.add(annotationInfo);
        }

        methodInfo.setAnnotationInfoList(annotationInfoList);
        methodInfo.setPackageName(packagePath);
        methodInfo.setClazzName(clazzName);
        methodInfo.setMethodName(method.getName());
        /**
         * 处理函数参数类型、默认值、参数注解
         */
        // 创建函数参数的信息对象集合
        List<ParamInfo> paramInfos = new ArrayList<>();
        // 创建函数参数默认值集合
        List<Object> paramValues = new ArrayList<>(paramInfos.size());
        // 创建函数参数类型集合
        List<Class<?>> paramTypes = new ArrayList<>(paramInfos.size());

        for (Class<?> paramType : method.getParameterTypes()) {
            ParamInfo paramInfo = new ParamInfo();

            // 设置函数参数类型
            paramInfo.setClazz(paramType);
            paramInfo.setClazzName(paramType.getTypeName());
            paramInfo.setAnnotationFlag(false);
            paramInfo.setAnnotationInfo(null);
            paramInfos.add(paramInfo);
            paramTypes.add(paramType);
            paramValues.add(setDefaultValue(paramInfo.getClazzName()));
        }

        /**
         * 为注解@ReqBody修饰的对象赋值
         */
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();

        int position = 0;

        for (Annotation[] annotations : parameterAnnotations) {
            if (annotations.length > 0) {
                ParamInfo paramInfo = paramInfos.get(position);
                if (annotations[0].annotationType().equals(ReqBody.class)) {
                    paramInfo.setAnnotationFlag(true);
                    paramInfo.setAnnotationInfo(new AnnotationInfo(ReqBody.class.getName()));
                }
            }

            position++;
        }

        methodInfo.setParamInfoList(paramInfos);
        methodInfo.setParamTypes(paramTypes);
        methodInfo.setParamValues(paramValues);
        // 生成全路径名，用|分割
        methodInfo.generateFullMethod();
    }

    private static Object setDefaultValue(String dataTypeName) {
        String[] dataTypes = {"byte", "short", "int", "long", "float", "double", "char", "boolean"};
        Object[] dataTypeValues = {0, 0, 0, 0L, 0.0f, 0.0d, '\u0000', false};

        for (int i = 0 ; i < dataTypes.length ; i++) {
            if (dataTypes[i].equals(dataTypeName)) {
                return dataTypeValues[i];
            }
        }

        return null;
    }


    /**
     * 扫描com.sunpy.niohttp.user包下面的类上面方法的注解
     * @return uri对应的方法映射
     */
    public static Map<String, MethodMapping> scanAnnotationToMethodMapping() {
        String userPackage = HttpPropertiesConfig.getUserPackage();


        try {
            if (userPackage != null && userPackage != "") {
                return scanAnnotationToMethodMapping(userPackage);
            }

            return scanAnnotationToMethodMapping(GENERAL_PACKAGE);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // throw new Exception("配置的用户使用包路径不存在")
        return null;
    }

    /**
     * 根据指定的uri、httpMethod查找对应MethodMapping映射信息
     * @param uri
     * @param httpMethod
     * @return
     */
    public static MethodMapping findMappingMethod(String uri, String httpMethod) {
        Map<String, MethodMapping> fullMethodMapping = scanAnnotationToMethodMapping();
        MethodMapping methodMapping = fullMethodMapping.get(uri);

        if (methodMapping == null) {
            System.out.println("该映射不存在, 404!");
            return null;
        }

        if (methodMapping.getHttpInfo().getHttpMethod().equalsIgnoreCase(httpMethod)) {
            return methodMapping;
        }

        return null;
    }

    public static void main(String[] args) throws Exception {
        MethodMapping methodMapping = AnnotationImpl.findMappingMethod("/getUserInfo",
                HTTP_Method.POST);
        System.out.println(methodMapping);
    }
}
